/*
Java board game library.
Copyright (C) 2011  Deepesh Garg

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

package d.g.boardgames.chess;

import java.util.ArrayList;
import java.util.List;

import d.g.boardgames.chess.ChessCellState.PieceColor;
import d.g.boardgames.chess.player.*;

public class ChessGame {

	private ChessBoard chessBoard;
	private ChessGameData gameData = new ChessGameData();
	
	private Player whitePlayer;
	private Player blackPlayer;

	private List<ChessGameListener> gameListenerList = new ArrayList<ChessGameListener>();
	
	
	GameRunner gameRunner;
	Thread gameThread;
	
	public ChessGame (Player whitePlayer, Player blackPlayer) {		
		chessBoard = new ChessBoard();
		this.whitePlayer = whitePlayer;
		this.blackPlayer = blackPlayer;
	}
	
	public ChessGame (Player whitePlayer, Player blackPlayer, ChessBoard chessBoard) {
		this.chessBoard = chessBoard;
		this.whitePlayer = whitePlayer;
		this.blackPlayer = blackPlayer;
	}
	
	public synchronized void startGame () {
		if (gameRunner == null) {
			gameRunner = new GameRunner();
			gameThread = new Thread(gameRunner);
			gameThread.setName("game-runner");
			gameThread.start();
		}
	}
	
	public synchronized void stopGame () {
		if (gameRunner != null) {
			gameRunner.keepRunning = false;
			whitePlayer.abortMove();
			blackPlayer.abortMove();
			gameRunner = null;
		}
	}

	public Player getWhitePlayer() {
		return whitePlayer;
	}

	public void setWhitePlayer(Player whitePlayer) {
		this.whitePlayer = whitePlayer;
	}

	public Player getBlackPlayer() {
		return blackPlayer;
	}

	public void setBlackPlayer(Player blackPlayer) {
		this.blackPlayer = blackPlayer;
	}
	
	public ChessGameData getGameData() {
		return gameData;
	}

	public void setGameData(ChessGameData gameData) {
		this.gameData = gameData;
		chessBoard = gameData.getLastEntry().getBoard();
		updateGameListeners();
	}
	
	public ChessBoard getChessBoard() {
		return chessBoard;
	}

	public void setChessBoard(ChessBoard chessBoard) {
		stopGame();
		gameData = new ChessGameData();
		this.chessBoard = chessBoard;
		gameData.addMoveData(chessBoard, null);
		updateGameListeners();
		startGame();
	}

	public void addGameListener (ChessGameListener gameListener) {
		gameListenerList.add(gameListener);
	}
	
	public void updateGameListeners () {
		if (gameListenerList != null) {
			for (ChessGameListener gameListener: gameListenerList) {
				gameListener.onGameUpdate(gameData);
			}
		}
	}

	public void forceMove(ChessMove chessMove) {
		gameRunner.setForcedMove(chessMove);
		whitePlayer.abortMove();
		blackPlayer.abortMove();
	}
	
	class GameRunner implements Runnable {
		boolean keepRunning = true;
		ChessMove forcedMove = null;
		
		public void run () {
			ChessMove chessMove = null;
			ChessBoard boardAfter = null;
			if (gameData.getLastEntry() == null || 
					(gameData.getLastEntry() != null && gameData.getLastEntry().getBoard() != chessBoard)) {
				gameData.getMoveDataList().clear();
				gameData.addMoveData(chessBoard, null);
			}
			while (keepRunning) {
				chessMove = null;
				if (chessBoard.getTurn() == PieceColor.WHITE) {
					chessMove = whitePlayer.getMove(chessBoard);
				} else {
					chessMove = blackPlayer.getMove(chessBoard);
				}
				if (forcedMove != null) {
					chessMove = forcedMove;
					forcedMove = null;
				}
				if (chessMove != null) {
					boardAfter = chessBoard.move(chessMove);
				}
				if (keepRunning && boardAfter != null) {
					gameData.getLastEntry().setMove(chessMove);
					gameData.addMoveData(boardAfter, null);
					chessBoard = boardAfter;
					updateGameListeners();
					boardAfter = null;
				}
			}
		}

		public void setForcedMove (ChessMove cm) {
			forcedMove = cm;
		}
	}
}
